﻿namespace EFS.Common.Async
{
	/// Class Repeater
	public abstract class RepeaterSlot : EFS.Common.Async.AsyncRoot
	{
		#region Composition

		// sync access
		private object syncAccess = new object();

		/// <summary>
		/// Cycels untill action
		/// </summary>
		private System.Int32 cycles = 0;

		/// <summary>
		/// Action cycle
		/// </summary>
		private System.Int32 actionSlotCycle = 1;

		// Status
		public enum SlotStatus { Idle, Active, Terminated }

		// Status
		private SlotStatus status = SlotStatus.Idle;

		// Action Status
		public enum SlotActionStatus { Idle, Started }

		// Action Status
		private SlotActionStatus actionStatus = SlotActionStatus.Idle;

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="actionSlotCycle"></param>
		protected RepeaterSlot(System.Int32 actionSlotCycle)
		{
			// check
			EFS.Common.Verify.Condition(actionSlotCycle >= 1, "actionSlotCycle >= 1");

			// set
			this.actionSlotCycle = actionSlotCycle;
		}

		#endregion

		#region Usage

		/// Status
		public SlotStatus Status { get { return status; } }

		/// <summary>
		/// Begin platform startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginStartup()
		{
			// log
			// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start end point
			return BeginAsyncProcess2(Startup, OnStartupComplete);
		}

		/// <summary>
		/// Begin platform startup
		/// </summary>
		public EFS.Common.Async.AsyncProcess BeginShutdown()
		{
			// log
			// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("BeginStartup", this);

			// start end point
			return BeginAsyncProcess2(Shutdown, OnShutdownComplete);
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Unhandled exception
		/// </summary>
		/// <param name="ex"></param>
		private void OnUnhadledException(System.Exception ex)
		{
			// check
			EFS.Common.Verify.Reference(ex, "ex");

			// log
			EFS.EventLog.Log.LogException(ex, this);

			// shut down
			BeginShutdown();
		}

		/// <summary>
		/// Repeate action
		/// </summary>
		protected abstract void RepeateAction();

		/// <summary>
		/// Method called by repeater
		/// </summary>
		internal void OnRepeate()
		{
			// start action flag
			System.Boolean startAction = false;

			// check status
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check wating
				if (actionStatus != SlotActionStatus.Started)
				{
					// increment cycles
					cycles++;

					// check if our slot
					if (cycles >= actionSlotCycle)
					{
						// reset cycles
						cycles = 0;

						// set active
						actionStatus = SlotActionStatus.Started;

						// start action
						startAction = true;
					}
				}
			}

			// check flag
			if (startAction)
			{
				// call repeate action
				BeginAsyncProcess2(RepeateAction, OnRepeateActionComplete);
			}
		}

		/// <summary>
		/// Actio complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnRepeateActionComplete(EFS.Common.Async.AsyncProcess ap)
		{
			try
			{
				// set status back to idle
				// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
				lock (syncAccess)
				{
					actionStatus = SlotActionStatus.Idle;
				}

				// check
				ap.Throw();
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion

		#region Startup

		/// <summary>
		/// Session startup
		/// </summary>
		private void Startup()
		{
			// log
			// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("Startup", this);

			// sync access
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check 
				if (status != SlotStatus.Idle && actionStatus != SlotActionStatus.Idle)
				{
					throw new System.InvalidOperationException("Only Idle Slot can be started");
				}

				// active
				status = SlotStatus.Active;
			}

			// setup timer
			Repeater.Instance.OnRepeate += OnRepeate;	
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnStartupComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("OnStartupComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("OnStartupComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion

		#region Shutdown

		/// <summary>
		/// Shut down
		/// </summary>
		public void Shutdown()
		{
			// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("Shutdown", this);

			// lock local acess
			// ~ commented for performance ~  EFS.EventLog.Log.LogExtended("Entering lock", new System.Diagnostics.StackTrace());
			lock (syncAccess)
			{
				// check already terminated
				if (status != SlotStatus.Terminated)
				{
					// set terinated
					status = SlotStatus.Terminated;
				}
				else
				{
					// already terminated
					return;
				}
			}

			// remove slot
			EFS.Common.Async.Repeater.Instance.OnRepeate -= OnRepeate;
		}

		/// <summary>
		/// Shut down complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnShutdownComplete(EFS.Common.Async.AsyncProcess ap)
		{
			// log
			// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("OnShutdownComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				// ~ commented for performance ~ EFS.EventLog.Log.LogInfo("OnShutdownComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion
	}
}